iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0
Software Development

一個好的系統之好維護基本篇 ( 馬克版 )系列 第 25

Day-25: 如何設計與管理 Bounded Context

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241009/20089358Vzw90IF2bD.png


在 Domain-Driven Design 中我會將 Bounded Context 拉出來的其中一個原因在於,如果你的公司是產品類且預期會慢慢往上發展的,那這個或需它是最重要的一個東西,它幾乎可以影響這間公司的系統未來幾年的基礎。

為什麼呢 ?

很多人應該都知道 BC 是切微服務的基礎,但是這裡也不是要談切微服務,而是當系統開始變大、人也開始變多以團隊也變多的情況下,你們要分職責的單位會是什麼。

這個就是我們家的血淚史,四個 team 都以專案導向,然後沒有什麼職責之分,結果就變成每個人都在打架、每個人都在認知負荷爆表,每個人最後都只想負責自已寫過的東西,因為他們也不知道自已的 team 是負責是什麼,就算要分也不知道如何分。

然後因為快受不了,就有將職責分下去。但認真的說我們家現在就算有職責分配與地盤劃分,也只是靠我們經驗來判斷,根本沒時間將整間公司 8 年的做過的東西拿來跑 Event Storming 找 BC,現在也只能針對那些要做的專案來跑,所以很多過去的東西是沒有 BC 的,但是我真的相信如果一開始有 BC 的話,我們就會更容易的來拆分,所以這一篇才將它單獨拿來說。


好的 Bounded Context 有什麼特點呢 ?

首先我們先來複習一下 Bounded Context 是啥 ?

比較工程術語就是,那些 Aggregate 所產生的 Domain Event 的職責邊界,也可以說就是 solution space ( 模型 )的解決問題的範圍

然後通常 problem space 的需求來後,我們應該會先看一下 solution space 能解決範圍,可不可以處理這個東西。

然後我自已整理出幾個我覺得好的 Bounded Context 特點

  1. 清晰的邊界與職責,越獨立越好
  2. 明確的知道在 Context Map 中的定位
  3. 不要太大

1. 清晰的邊界與職責,越獨立越好

理想上我們的 BC 可以越獨立運作越好,但這裡也不是說不要合作,因為如果它不獨立,它做什麼事都依賴其它 BC,或是和其它 BC 職責重複,那這樣沒打起來就很好了,還合作 ?

但要如何判斷它多獨立呢 ? 我自已的想法是可以用以下幾點來判斷 :

  • 你依賴了多少外部 BC ? 像如果你是用 nestjs 開發,它有 module 的概念,你知道看你的 BC import 多少其它 BC 的 module 你就會知道你能多獨立了,越多代表越不獨立。
  • 你這個 BC 被多少 BC 使用呢 ?

那要如何判斷它有沒有和其它 BC Cover 到呢 ? 我自已想法就是你發現有個 Domain Event 放那都行,1 個應該還好,但是如果是很多個,那基本上就是這兩個 cover 很多,那這時一定要討論雙方的定位了。

下面就是一個不好的 BC 的範例,對吧 ? 那些 Domain Event 我們都不知道要放那了 ?

https://ithelp.ithome.com.tw/upload/images/20241009/20089358ZdvfxQUwEs.png

備註 1:
但這裡並不代表不能 cover,而是越小越好,然後真的有的話還是可以用一些手法來處理,例如 Shared Kernel 就是來處理這個情況。

備註 2:
事實上這東西也可以拿 SRP 的那張圖來看,修改的理由越少越好。

2. 明確的知道在 Context Map 中的定位

先說一下 Context Map 就是所有的 BC 關係圖,例如下圖,他們就很明確知道不同 BC 的關係,如果你發現你產出來的 BC 不知道發在這裡面那裡,但它明明又沒那們獨立,那就有問題了。

https://ithelp.ithome.com.tw/upload/images/20241009/20089358ra4Qv9JCOr.png
圖片來源: https://www.researchgate.net/figure/Example-map-teams-realize-bounded-contexts_fig13_337733330

3. 不要太大

因為之前有提到 Bounded Context 本身就是個切分團隊職責的一種單位,如果太大就代表 :

這個團隊的認知負荷與職責非常高

然後我在看《 Team Topologies: Organizing Business and Technology Teams for Fast Flow 》它就有提到 Domain 不能太大,不然也不知道怎麼分。如下圖如果我將 domain 2 + 4 分給一個 team,那個 team 應該就會死。

https://ithelp.ithome.com.tw/upload/images/20241009/20089358M2iZC47gWk.png
圖片來源: Team Topologies


如何找出 Bounded Context 呢 ?

我先說一下我們家的場景,他就是我自已做的產品,然後我自已的認知是我們這裡沒有所謂的 domain expert,因為所有東西的發想都是上面想要做的,除非是一些比較特殊的例如和財務有關的才可能有他們當 domain expert,但有 90% 的情況下,最熟細 domain 的人就是要做那個東西的人,通常就是 PM + 上面 + 營運提出的需求。

然後我自已覺得在跑 event storming 可以嘗試用以下的問題來找出有沒有適合的 bounded context :

1. 我們可以用 Actor 來分嗎 ?

然後通常就有人問那這樣 2 個不同 actor 有同樣事件怎麼辦 ? 嗯很棒那就代表這個選項可以先 wait 看看之後有沒有更好的,像我們家用 student 與 creator 來分,事實上我發現也算適合,因為重複的事件很少,但不是說沒有。

而且某些方面這也是《 Clean Architecture 》提的 SRP 概念:

一個元件只有一個角色引起變化

有點感覺對吧 ?

2. 我們可以用業務來分嗎 ?

這裡可以想成就是,我們 event storming 完後,不是找出很多 domain event 嗎 ? 那接下來可不可以根據這個 domain event 分成比較接近的群組呢 ?

例如我們找出了以下的 domain event :

  • OrderPlaced(訂單已下)
  • PaymentProcessed(付款已處理)
  • InventoryChecked(庫存已檢查)
  • OrderShipped(訂單已發貨)
  • OrderDelivered(訂單已送達)
  • ProductAddedToCart(商品已加入購物車)
  • ProductRemovedFromCart(商品已從購物車移除)
  • PaymentFailed(付款失敗)
  • OrderCancelled(訂單已取消)
  • ProductReviewed(商品已評價)

那是不是可以大概先分以下 2 個?

  • 購物與訂單處理上下文 (Shopping and Order Processing Context)
    • 除了 ProductReviewed
  • 商品評價上下文 (Product Review Context)
    • ProductReviewed

先不討論第 2 個太小還啥,這個範例就是使用業務情境來進行區分。

3. 我們可以用團隊結構來分嗎 ?

以正常的情況下,團隊組織本質在於未來的目標、職責、溝通,其中溝通這一塊是不是很像我們上面提到的『 越獨立獨好 』,因為你自已想想如果 2 個部門天天都需要合作,那為什麼他們要分 2 個部門呢 ? 除非是財務預算分配的問題,但這就是另一題了。

然後這裡通常可以先用現有團隊來看看有沒有適合的。

備註:
然後這裡有一些 keyword 和這個地方有關,有興趣的朋朋們可以去查查。

  • 康威定律
  • 逆康威

4. 有沒有那些 Domain Event 是在舊有的 Bounded Context 出現過呢 ?

通常這種情況代表可以放到舊的那個 Bounded-Context,但也有可能是之前那個要移出來。

5. 有沒有那些 domain event 你覺得是很通用的,可以讓其它 Bounded Context 使用的 ?

這個就是找一些通用行的事件之類的,例如用戶已上傳影片、圖片之類的。


Bounded Context 如何演進呢 ?

嚴格來說我只有切 Bounded Context 的經驗,但是我沒有演進過 Bounded Context 的經驗,所以這裡大部份是我自已調查,還是在思考的過程中會想到的問題與可能解法。

什麼時後要重構 Bounded Context 呢 ?

我自已覺得以下幾種情況下就需要了 :

  1. 負責那個 BC 的團隊的人認知負擔太大了。
  2. 我們發現那個 BC 要完成一件事情,需要一直和其它 BC 合作,也就是說沒有其它 BC 他自已也沒辦法做事。
  3. 太常分不清那個 domain event 該放那。

第一種情況下我自已的想法就是將我們的 BC 都拿出來看,看看是不是那個 BC 的職責和其它的 cover 到太多,或是職責太少,事實上可以合在一起。

然後第二種情況就是,高度依賴性然後還拆成不同的後果,記得 CRP 原則嗎 ? 經常一起被使用的東西應該要放在一起。

然後最後一種情況,就是如果太複雜,那是不是就要將他變小,或是讓那個負責團隊所維護的 BC 數量減少呢?

重構時可能會需要動到什麼? 

如果 BC 名變了的話,我自已腦袋想到的是會變以下幾個:

  1. API 的變動
  2. Domain Event 的變動
  3. 資料庫的變動
  4. 團隊職責

會這想是因為,通常我們根據 BC 來開 API 時,如果是微服務通常就是可能會有不同的 subdomain,又或是有個 gateway 可以轉到那個微服務,如果這時有 api gateway 那應該將那個 router 改成新名字的 domain。

但如果是同一個服務,只是內部份 BC 的情況下,通常可能會命名 :

/{bounded-context}/{aggregate}

那這樣是不是 path 也都要改了 ? 這裡比較好的解法我自已覺得就是在 migration 的期間同時保留新舊 2 個路徑。

然後其它 2、3 事實上概念也都是相同的,總結來說三個要做的事都為 :

  1. API 路徑 確實需要根據新的 BC 名稱進行變更,特別是對於 API Gateway 路由的設置。
  2. Domain Event 的名稱可能需要改變,並且事件消費者也需要同步更新。
  3. 資料庫 需要根據新的 BC 名稱來改變表名或集合名,並進行數據遷移和更新。

然後最後一個就是要佈達說團隊地盤變動囉 ~

所以事實上重構 Bounded Context 的工事實上不小啊


Bounded Context 大小

正常情況我建議一開始先從大在切小,然後再慢慢拆 ( 就像是大泥球解耦 )

然後這是我自已寫下的拆分準則,可以來拿參考就好 :

  1. 這個服務的變化,是否是很容易影響其它服務,如果是的話那就不符合 ( CCP )。
  2. 避免循環依賴,切分後還有循環依懶,那也只是個分散式單體 ( ADP )。
  3. 依賴穩定,可以有依賴,但要有方向 ( Context Map )。
  4. 一個服務『 所要完成的功能 』 如果需要依賴很多服務才能完成,那他就沒有高內聚 ( SDP )。
  5. 判斷是否有明確的 Bounded Context 邊界。
  6. 是否有團隊自治性 ( Autonomy ) ? ( 部暑是否獨立 + 這個服務的歸屬 )
  7. 在沒有性能壓力下,這個服務會不會太小 ? 導致完成某項需求後就沒有在增加新功能過,導致浪費資源維護服務。

小結

在 Domain-Driven Design 中, Bounded Context 是一個非常重要的概念,尤其當公司預期有較長的發展時,它能讓這間公司的系統與團隊建立明確的邊界與基礎。

然後這裡我們探討了 4 個重點 :

  • 好的 Bounded Context 有什麼特點呢 ?
  • 如何找出 Bounded Context 呢 ?
  • Bounded Context 如何演進呢 ?
  • Bounded Context 大小

不過說實話,我到現在還是覺得這東西還是只能抓個大概 ~ 尤其是到新公司的話又是大洗牌一次,某些方面我真的覺得一間公司待越久的後端,一定是他們家的 domain expert,我現在很明顯有這種感受 ( PM、營運、工程、財務 ) 都會來問你 domain。


上一篇
Day-24: Domain Driven Design 與 API 設計的難處
下一篇
Day-26: CQRS ( Command Query Responsibility Segregation )
系列文
一個好的系統之好維護基本篇 ( 馬克版 )30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言